#region Copyright (c) 2006-2009 Widgetsphere LLC, All Rights Reserved
//--------------------------------------------------------------------- *
//                          Widgetsphere  LLC                           *
//             Copyright (c) 2006-2009 All Rights reserved              *
//                                                                      *
//                                                                      *
//This file and its contents are protected by United States and         *
//International copyright laws.  Unauthorized reproduction and/or       *
//distribution of all or any portion of the code contained herein       *
//is strictly prohibited and will result in severe civil and criminal   *
//penalties.  Any violations of this copyright will be prosecuted       *
//to the fullest extent possible under law.                             *
//                                                                      *
//THE SOURCE CODE CONTAINED HEREIN AND IN RELATED FILES IS PROVIDED     *
//TO THE REGISTERED DEVELOPER FOR THE PURPOSES OF EDUCATION AND         *
//TROUBLESHOOTING. UNDER NO CIRCUMSTANCES MAY ANY PORTION OF THE SOURCE *
//CODE BE DISTRIBUTED, DISCLOSED OR OTHERWISE MADE AVAILABLE TO ANY     *
//THIRD PARTY WITHOUT THE EXPRESS WRITTEN CONSENT OF WIDGETSPHERE LLC   *
//                                                                      *
//UNDER NO CIRCUMSTANCES MAY THE SOURCE CODE BE USED IN WHOLE OR IN     *
//PART, AS THE BASIS FOR CREATING A PRODUCT THAT PROVIDES THE SAME, OR  *
//SUBSTANTIALLY THE SAME, FUNCTIONALITY AS ANY WIDGETSPHERE PRODUCT.    *
//                                                                      *
//THE REGISTERED DEVELOPER ACKNOWLEDGES THAT THIS SOURCE CODE           *
//CONTAINS VALUABLE AND PROPRIETARY TRADE SECRETS OF WIDGETSPHERE,      *
//INC.  THE REGISTERED DEVELOPER AGREES TO EXPEND EVERY EFFORT TO       *
//INSURE ITS CONFIDENTIALITY.                                           *
//                                                                      *
//THE END USER LICENSE AGREEMENT (EULA) ACCOMPANYING THE PRODUCT        *
//PERMITS THE REGISTERED DEVELOPER TO REDISTRIBUTE THE PRODUCT IN       *
//EXECUTABLE FORM ONLY IN SUPPORT OF APPLICATIONS WRITTEN USING         *
//THE PRODUCT.  IT DOES NOT PROVIDE ANY RIGHTS REGARDING THE            *
//SOURCE CODE CONTAINED HEREIN.                                         *
//                                                                      *
//THIS COPYRIGHT NOTICE MAY NOT BE REMOVED FROM THIS FILE.              *
//--------------------------------------------------------------------- *
#endregion
using System;
using System.Xml;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using Widgetsphere.Generator.Common.GeneratorFramework;
using Widgetsphere.Generator.Common.Util;
using System.Linq;

namespace Widgetsphere.Generator.Models
{
  public class Database : BaseModelObject
  {
    #region Member Variables

    protected const string _def_createdByColumnName = "created_by";
    protected const string _def_createdDateColumName = "created_date";
    protected const string _def_modifiedByColumnName = "modified_by";
    protected const string _def_modifiedDateColumnName = "modified_date";
    protected const string _def_timestampColumnName = "time_stamp";
		protected const string _def_fullIndexSearchColumnName = "full_index_text";
		protected const bool _def_allowZeroTouch = false;

    protected string _createdByColumnName = _def_createdByColumnName;
    protected string _createdDateColumName = _def_createdDateColumName;
    protected string _modifiedByColumnName = _def_modifiedByColumnName;
    protected string _modifiedDateColumnName = _def_modifiedDateColumnName;
    protected string _fullIndexSearchColumnName = _def_fullIndexSearchColumnName;
    protected string _timestampColumnName = _def_timestampColumnName;
		protected string _grantExecUser = "";
		protected bool _allowZeroTouch = _def_allowZeroTouch;

    protected string _databaseName = "";
    protected TableCollection _tables = null;
    protected ColumnCollection _columns = null;
    protected RelationCollection _relations = null;
		protected CustomViewCollection _customViews = null;
		protected CustomAggregateCollection _customAggregates = null;
    protected CustomStoredProcedureCollection _customStoredProcedures = null;
		protected CustomViewColumnCollection _customViewColumns = null;
		protected CustomAggregateColumnCollection _customAggregateColumns = null;
    protected CustomStoredProcedureColumnCollection _customStoredProcedureColumns = null;
    protected CustomRetrieveRuleCollection _customRetrieveRules = null;
    protected ParameterCollection _customRetrieveRuleParameters = null;
    private string _tablePrefix = "";
		private DateTime _createdDate = DateTime.Now;

    #endregion

    #region Constructor

    public Database(IModelObject root)
      : base(root)
    {
      _tables = new TableCollection(this.Root);
      _columns = new ColumnCollection(this.Root);
      _relations = new RelationCollection(this.Root);
			_customViews = new CustomViewCollection(this.Root);
			_customAggregates = new CustomAggregateCollection(this.Root);
      _customStoredProcedures = new CustomStoredProcedureCollection(this.Root);
			_customViewColumns = new CustomViewColumnCollection(this.Root);
			_customAggregateColumns = new CustomAggregateColumnCollection(this.Root);			
      _customStoredProcedureColumns = new CustomStoredProcedureColumnCollection(this.Root);
      _customRetrieveRules = new CustomRetrieveRuleCollection(this.Root);
      _customRetrieveRuleParameters = new ParameterCollection(this.Root);
    }

    #endregion

    #region Property Implementations

    [Browsable(false)]
    public ColumnCollection Columns
    {
      get { return _columns; }
    }

    [Browsable(false)]
    public RelationCollection Relations
    {
      get { return _relations; }
    }

    [Browsable(true),
    Description("Determines the name of the database."),
    Category("Data")]
    public string DatabaseName
    {
      get { return _databaseName; }
      set
      {
        _databaseName = value;
        this.OnPropertyChanged(this, new PropertyChangedEventArgs("DatabaseName"));
      }
    }

    [Browsable(true),
    Description("Determines the name of the created by column name."),
    Category("Data")]
    public string CreatedByColumnName
    {
      get { return _createdByColumnName; }
      set
      {
        _createdByColumnName = value;
        this.OnPropertyChanged(this, new PropertyChangedEventArgs("CreatedByColumnName"));
      }
    }

    [Browsable(true),
    Description("Determines the name of the created date column."),
    Category("Data")]
    public string CreatedDateColumnName
    {
      get { return _createdDateColumName; }
      set
      {
        _createdDateColumName = value;
        this.OnPropertyChanged(this, new PropertyChangedEventArgs("CreatedDateColumnName"));
      }
    }

    [Browsable(true),
    Description("Determines the name of the modified by column."),
    Category("Data")]
    public string ModifiedByColumnName
    {
      get { return _modifiedByColumnName; }
      set
      {
        _modifiedByColumnName = value;
        this.OnPropertyChanged(this, new PropertyChangedEventArgs("ModifiedByColumnName"));
      }
    }

    [Browsable(true),
    Description("Determines the name of the modified date column."),
    Category("Data")]
    public string ModifiedDateColumnName
    {
      get { return _modifiedDateColumnName; }
      set
      {
        _modifiedDateColumnName = value;
        this.OnPropertyChanged(this, new PropertyChangedEventArgs("ModifiedDateColumnName"));
      }
    }

		/// <summary>
		/// This not implemented
		/// </summary>
		[Browsable(false),
    Description("Determines the name of the full index search column."),
    Category("Data")]
    public string FullIndexSearchColumnName
    {
      get { return _fullIndexSearchColumnName; }
      set
      {
        _fullIndexSearchColumnName = value;
        this.OnPropertyChanged(this, new PropertyChangedEventArgs("FullIndexSearchColumnName"));
      }
    }

    [Browsable(true),
    Description("Determines the name of the timestamp column."),
    Category("Data")]
    public string TimestampColumnName
    {
      get { return _timestampColumnName; }
      set
      {
        _timestampColumnName = value;
        this.OnPropertyChanged(this, new PropertyChangedEventArgs("TimestampColumnName"));
      }
    }

    [
    Category("Data"),
    TypeConverter(typeof(Widgetsphere.Generator.Design.Converters.TableCollectionConverter)),
    Editor(typeof(Widgetsphere.Generator.Design.Editors.TableCollectionEditor), typeof(System.Drawing.Design.UITypeEditor))
    ]
    public TableCollection Tables
    {
      get { return _tables; }
    }

    [
    Category("Data"),
    TypeConverter(typeof(Widgetsphere.Generator.Design.Converters.CustomViewCollectionConverter)),
    Editor(typeof(Widgetsphere.Generator.Design.Editors.CustomViewCollectionEditor), typeof(System.Drawing.Design.UITypeEditor))
    ]
    public CustomViewCollection CustomViews
    {
      get { return _customViews; }
    }

		[
		Category("Data"),
		TypeConverter(typeof(Widgetsphere.Generator.Design.Converters.CustomAggregateCollectionConverter)),
		Editor(typeof(Widgetsphere.Generator.Design.Editors.CustomAggregateCollectionEditor), typeof(System.Drawing.Design.UITypeEditor))
		]
		public CustomAggregateCollection CustomAggregates
		{
			get { return _customAggregates; }
		}

    [
    Category("Data"),
    TypeConverter(typeof(Widgetsphere.Generator.Design.Converters.CustomStoredProcedureCollectionConverter)),
    Editor(typeof(Widgetsphere.Generator.Design.Editors.CustomStoredProcedureCollectionEditor), typeof(System.Drawing.Design.UITypeEditor))
    ]
    public CustomStoredProcedureCollection CustomStoredProcedures
    {
      get { return _customStoredProcedures; }
    }    

    [Browsable(false)]
    public CustomViewColumnCollection CustomViewColumns
    {
      get { return _customViewColumns; }
    }

		[Browsable(false)]
		public CustomAggregateColumnCollection CustomAggregateColumns
		{
			get { return _customAggregateColumns; }
		}

    [Browsable(false)]
    public CustomStoredProcedureColumnCollection CustomStoredProcedureColumns
    {
      get { return _customStoredProcedureColumns; }
    }

    [Browsable(false)]
    public CustomRetrieveRuleCollection CustomRetrieveRules
    {
      get { return _customRetrieveRules; }
    }

    [Browsable(false)]
    public ParameterCollection CustomRetrieveRuleParameters
    {
      get { return _customRetrieveRuleParameters; }
    }

		[Browsable(true),
	  Description("Determines the database user to grant execution permissions to for the stored procedures."),		
	  Category("Data")]
		public string GrantExecUser
		{
			get { return _grantExecUser; }
			set
			{
				_grantExecUser = value;
				this.OnPropertyChanged(this, new PropertyChangedEventArgs("GrantExecUser"));
			}
		}

		[Browsable(false),
	  Description("Determines if anything must be installed in the databae to upport this model."),
		DefaultValue(_def_allowZeroTouch),
	  Category("Data")]
		public bool AllowZeroTouch
		{
			get { return _allowZeroTouch; }
			set
			{
				_allowZeroTouch = value;
				this.OnPropertyChanged(this, new PropertyChangedEventArgs("AllowZeroTouch"));
			}
		}		

    [Browsable(true),
    Description("Determines the prefix to prepend to all table names. This can be used if you have many models generated to one database."),
    Category("Data")]
    public string TablePrefix
    {
      get { return _tablePrefix; }
      set
      {
        _tablePrefix = value;
        this.OnPropertyChanged(this, new PropertyChangedEventArgs("TablePrefix"));
      }
    }

		[Browsable(true)]
		[Category("Data")]
		[Description("The date that this object was created.")]
		[ReadOnlyAttribute(true)]
		public DateTime CreatedDate
		{
			get { return _createdDate; }
		}

		#endregion

    #region Methods

    protected internal bool RelationRoleExists(string roleName, Relation skipItem)
    {
      foreach(Relation relation in this.Relations)
      {
        //If this is not the skip item and the role matches then return true
        if((relation != skipItem) && (StringHelper.Match(relation.RoleName, roleName, true)))
        {
          return true;
        }
      }
      return false;
    }

		internal IEnumerable<Table> GetGeneratedTables()
		{
			return (from x in this.Tables where x.Generated select x);
		}

    #endregion

    #region IXMLable Members

    public override void XmlAppend(XmlNode node)
    {
      try
      {
        XmlDocument oDoc = node.OwnerDocument;

        XmlAttribute keyNode = oDoc.CreateAttribute("key");
        keyNode.Value = this.Key.ToString();
        node.Attributes.Append(keyNode);

        XmlHelper.AddAttribute((XmlElement)node, "createdByColumnName", CreatedByColumnName);
        XmlHelper.AddAttribute((XmlElement)node, "createdDateColumnName", CreatedDateColumnName);
        XmlHelper.AddAttribute((XmlElement)node, "modifiedByColumnName", ModifiedByColumnName);
        XmlHelper.AddAttribute((XmlElement)node, "modifiedDateColumnName", ModifiedDateColumnName);
        XmlHelper.AddAttribute((XmlElement)node, "timestampColumnName", TimestampColumnName);
        XmlHelper.AddAttribute((XmlElement)node, "fullIndexSearchColumnName", FullIndexSearchColumnName);
				XmlHelper.AddAttribute((XmlElement)node, "grantExecUser", GrantExecUser);
				XmlHelper.AddAttribute((XmlElement)node, "allowZeroTouch", AllowZeroTouch.ToString());
        XmlHelper.AddAttribute((XmlElement)node, "tablePrefix", TablePrefix);

        XmlNode columnsNode = oDoc.CreateElement("columns");
        Columns.XmlAppend(columnsNode);
        node.AppendChild(columnsNode);

        XmlNode customViewColumnsNode = oDoc.CreateElement("customviewcolumns");
        this.CustomViewColumns.XmlAppend(customViewColumnsNode);
        node.AppendChild(customViewColumnsNode);

				XmlNode customAggregateColumnsNode = oDoc.CreateElement("customaggregatecolumns");
				this.CustomAggregateColumns.XmlAppend(customAggregateColumnsNode);
				node.AppendChild(customAggregateColumnsNode);
				
				XmlNode customStoredProcedureColumnsNode = oDoc.CreateElement("customstoredprocedurecolumns");
        this.CustomStoredProcedureColumns.XmlAppend(customStoredProcedureColumnsNode);
        node.AppendChild(customStoredProcedureColumnsNode);

        XmlNode customRetrieveRulesNode = oDoc.CreateElement("customretrieverules");
        this.CustomRetrieveRules.XmlAppend(customRetrieveRulesNode);
        node.AppendChild(customRetrieveRulesNode);

        XmlNode customRetrieveRuleParameterNode = oDoc.CreateElement("customretrieveruleparameters");
        this.CustomRetrieveRuleParameters.XmlAppend(customRetrieveRuleParameterNode);
        node.AppendChild(customRetrieveRuleParameterNode);

        XmlNode relationsNode = oDoc.CreateElement("relations");
        Relations.XmlAppend(relationsNode);
        node.AppendChild(relationsNode);

        XmlAttribute databaseName = oDoc.CreateAttribute("databaseName");
        databaseName.Value = this.DatabaseName;
        node.Attributes.Append(databaseName);


        XmlNode tablesNode = oDoc.CreateElement("tables");
        this.Tables.XmlAppend(tablesNode);
        node.AppendChild(tablesNode);

        XmlNode customViewsNode = oDoc.CreateElement("customviews");
        this.CustomViews.XmlAppend(customViewsNode);
        node.AppendChild(customViewsNode);

				XmlNode customAggregatesNode = oDoc.CreateElement("customaggregates");
				this.CustomAggregates.XmlAppend(customAggregatesNode);
				node.AppendChild(customAggregatesNode);

        XmlNode customStoredProceduresNode = oDoc.CreateElement("customstoredprocedures");
        this.CustomStoredProcedures.XmlAppend(customStoredProceduresNode);
        node.AppendChild(customStoredProceduresNode);

				XmlAttribute created = oDoc.CreateAttribute("createdDate");
				created.Value = _createdDate.ToString("yyyy-MM-dd HH:mm:ss");
				node.Attributes.Append(created);

      }
      catch(Exception ex)
      {
        throw;
      }

    }

    public override void XmlLoad(XmlNode node)
    {
      try
      {
        _key = XmlHelper.GetAttributeValue(node, "key", _key);

				XmlNode relationsNode = node.SelectSingleNode("relations");
				if (relationsNode != null)
					this.Relations.XmlLoad(relationsNode);

        XmlNode tablesNode = node.SelectSingleNode("tables");
        if(tablesNode != null)
          this.Tables.XmlLoad(tablesNode);

        XmlNode customViewsNode = node.SelectSingleNode("customviews");
        if(customViewsNode != null)
          this.CustomViews.XmlLoad(customViewsNode);

				XmlNode customAggregatesNode = node.SelectSingleNode("customaggregates");
				if (customAggregatesNode != null)
					this.CustomAggregates.XmlLoad(customAggregatesNode);

        XmlNode customStoredProceduresNode = node.SelectSingleNode("customstoredprocedures");
        if(customStoredProceduresNode != null)
          this.CustomStoredProcedures.XmlLoad(customStoredProceduresNode);

        XmlNode customRetrieveRulesNode = node.SelectSingleNode("customretrieverules");
        if(customRetrieveRulesNode != null)
          this.CustomRetrieveRules.XmlLoad(customRetrieveRulesNode);

        XmlNode customRetrieveRulesParameterNode = node.SelectSingleNode("customretrieveruleparameters");
        if(customRetrieveRulesParameterNode != null)
          this.CustomRetrieveRuleParameters.XmlLoad(customRetrieveRulesParameterNode);        

        XmlNode columnsNode = node.SelectSingleNode("columns");
        if(columnsNode!= null)
          this.Columns.XmlLoad(columnsNode);

				//List<int> ll = new List<int>();
				//foreach(Table t in this.Tables)
				//{
				//  foreach (Reference r in t.Columns)
				//  {
				//    if (ll.Contains(((Column)r.Object).Id))
				//    {
				//      int ii = 0;
				//    }
				//    ll.Add(((Column)r.Object).Id);
				//  }
				//}

        XmlNode customviewcolumnsNode = node.SelectSingleNode("customviewcolumns");
        if(customviewcolumnsNode != null)
          this.CustomViewColumns.XmlLoad(customviewcolumnsNode);

				XmlNode customAggregatecolumnsNode = node.SelectSingleNode("customaggregatecolumns");
				if (customAggregatecolumnsNode != null)
					this.CustomAggregateColumns.XmlLoad(customAggregatecolumnsNode);

        XmlNode customstoredprocedurecolumnsNode = node.SelectSingleNode("customstoredprocedurecolumns");
        if(customstoredprocedurecolumnsNode != null)
          this.CustomStoredProcedureColumns.XmlLoad(customstoredprocedurecolumnsNode);

				//Clean all tables that are dead
				foreach (Column column in this.Columns)
				{
				}

				List<CustomRetrieveRule> deletedRules = new List<CustomRetrieveRule>();
				foreach (CustomRetrieveRule rule in this.CustomRetrieveRules)
				{
					Table table = (Table)rule.ParentTableRef.Object;
					if (table == null)
					{
						deletedRules.Add(rule);
					}
					else if (!table.CustomRetrieveRules.Contains(rule.Key))
					{
						deletedRules.Add(rule);
					}
				}

				foreach (CustomRetrieveRule rule in deletedRules)
				{
					this.CustomRetrieveRules.Remove(rule);
				}

				foreach (Table table in this.Tables)
				{
					List<Reference> deletedReferences = new List<Reference>();
					foreach (Reference reference in table.CustomRetrieveRules)
					{
						if (reference.Object == null)
						{
							deletedReferences.Add(reference);
						}
						else if (!this.CustomRetrieveRules.Contains(reference.Object.Key))
						{
							deletedReferences.Add(reference);
						}
					}

					foreach (Reference reference in deletedReferences)
					{
						table.CustomRetrieveRules.Remove(reference);
					}

				}

				//Clean all tables that are dead
				foreach (CustomStoredProcedureColumn column in this.CustomStoredProcedureColumns)
				{					
				}

				foreach (CustomViewColumn column in this.CustomViewColumns)
				{
				}

        this.DatabaseName = node.Attributes["databaseName"].Value;

        this.CreatedByColumnName = XmlHelper.GetAttributeValue(node, "createdByColumnName", _def_createdByColumnName);
        this.CreatedDateColumnName = XmlHelper.GetAttributeValue(node, "createdDateColumnName", _def_createdDateColumName);
        this.ModifiedByColumnName = XmlHelper.GetAttributeValue(node, "modifiedByColumnName", _def_modifiedByColumnName);
        this.ModifiedDateColumnName = XmlHelper.GetAttributeValue(node, "modifiedDateColumnName", _def_modifiedDateColumnName);
        this.TimestampColumnName = XmlHelper.GetAttributeValue(node, "timestampColumnName", _def_timestampColumnName);
        this.FullIndexSearchColumnName = XmlHelper.GetAttributeValue(node, "fullIndexSearchColumnName", _def_fullIndexSearchColumnName);
				this.GrantExecUser = XmlHelper.GetAttributeValue(node, "grantExecUser", "");
				this.AllowZeroTouch = bool.Parse(XmlHelper.GetAttributeValue(node, "allowZeroTouch", _allowZeroTouch.ToString()));
				
        this.TablePrefix = XmlHelper.GetAttributeValue(node, "tablePrefix", "");

				_createdDate = DateTime.ParseExact(XmlHelper.GetAttributeValue(node, "createdDate", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")), "yyyy-MM-dd HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture);

				this.Dirty = false;

				#region Are any of these columns orphans
				List<Column> deleteColumnList = new List<Column>();
				int index = 0;
				IEnumerable<Column> allColumns = this.Tables.GetAllColumns();
				foreach (Column column in this.Columns)
				{
					if (!allColumns.Contains(column))
					{
						index++;
						deleteColumnList.Add(column);
						System.Diagnostics.Debug.Write("");
					}
				}

				foreach (Column column in deleteColumnList)
					this.Columns.Remove(column);

				#endregion

				#region Clean relations in case there are dead ones
				List<Relation> deleteRelationList = new List<Relation>();
				foreach (Relation relation in this.Relations)
				{
					if ((relation.ParentTableRef == null) || (relation.ChildTableRef == null))
					{
						if (!deleteRelationList.Contains(relation)) deleteRelationList.Add(relation);
					}
					else
					{
						Table t = (Table)relation.ParentTableRef.Object;
						if (!t.Relationships.Contains(relation.Id))
						{
							if (!deleteRelationList.Contains(relation)) deleteRelationList.Add(relation);
						}
					}
				}

				//Now do the actual deletes
				foreach (Relation relation in deleteRelationList)
				{
					this.Relations.Remove(relation);
				}
				#endregion

			}
      catch(Exception ex)
      {
        throw;
      }
    }

    #endregion

  }
}